/*
 * Implementacion de Automata Finito para reconocer comentarios.
 * 
 * Basado en el proyecto descrito en: 
 * Louden, K. (1997). Compiler Construction: Principles and Practice. Boston: PWS.
 * 
 * @ Project: Compiler
 * @ File Name: CommentAutomaton.java
 */
package cml.scanning.automata;

import cml.TokenType;

public class CommentAutomaton extends Automaton{

	private enum StateType{START, ENTERING_COMMENT, IN_COMMENT, EXITING_COMMENT, DONE, ERROR};
	private StateType state;
	
	/*
	 * Constructor de la clase.
	 */
	public CommentAutomaton(){
		
		this.state = StateType.START;
		
		/*
		 * 						/						*					other
		 * START			|	ENTERING_COMMENT	|	NO_BRA			|	NO_BRA
		 * ENTERING_COMMENT	|	[DONE]				|	IN_COMMENT		|	[DONE]
		 * IN_COMMENT		|	IN_COMMENT			|	EXITING_COMMENT |	IN_COMMENT
		 * EXITING_COMMENT	|	START				|	EXITING_COMMENT	|	IN_COMMENT
		 * DONE				|	ERROR				|	ERROR			|	ERROR
		 * ERROR			|	NO_BRA				|	NO_BRA			|	NO_BRA
		 */
		
		this.successorFunc = new Branch[6][3];
		this.successorFunc[0][0] = new Branch(StateType.ENTERING_COMMENT.ordinal(), false);
		this.successorFunc[0][1] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[0][2] = new Branch(this.NO_BRANCH, false);
		
		this.successorFunc[1][0] = new Branch(StateType.DONE.ordinal(), false);
		this.successorFunc[1][1] = new Branch(StateType.IN_COMMENT.ordinal(), false);
		this.successorFunc[1][2] = new Branch(StateType.DONE.ordinal(), false);
		
		this.successorFunc[2][0] = new Branch(StateType.IN_COMMENT.ordinal(), true);
		this.successorFunc[2][1] = new Branch(StateType.EXITING_COMMENT.ordinal(), false);
		this.successorFunc[2][2] = new Branch(StateType.IN_COMMENT.ordinal(), true);
		
		this.successorFunc[3][0] = new Branch(StateType.START.ordinal(), false);
		this.successorFunc[3][1] = new Branch(StateType.EXITING_COMMENT.ordinal(), false);
		this.successorFunc[3][2] = new Branch(StateType.IN_COMMENT.ordinal(), true);
		
		this.successorFunc[4][0] = new Branch(StateType.ERROR.ordinal(), false);
		this.successorFunc[4][1] = new Branch(StateType.ERROR.ordinal(), false);
		this.successorFunc[4][2] = new Branch(StateType.ERROR.ordinal(), false);

		this.successorFunc[5][0] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[5][1] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[5][2] = new Branch(this.NO_BRANCH, false);
		
		this.acceptState = new boolean[StateType.values().length];
		this.acceptState[StateType.START.ordinal()] = true;
		this.acceptState[StateType.ENTERING_COMMENT.ordinal()] = false;
		this.acceptState[StateType.IN_COMMENT.ordinal()] = false;
		this.acceptState[StateType.EXITING_COMMENT.ordinal()] = false;
		this.acceptState[StateType.DONE.ordinal()] = true;
		this.acceptState[StateType.ERROR.ordinal()] = false;
		
		this.tokenString = "";
		this.backtrack = false;
	}
	
	/*
	 * Ejecuta la siguiente transicion (si la hay) leyendo el caracter pasado 
	 * como parametro.
	 * 
	 * @param	char	el siguiente caracter a leer
	 * @return			<code>true</code> si ejecuto una transicion; 
	 * 					<code>false</code> en caso contrario
	 */
	@Override
	public boolean nextStep(char c){
		
		int idx2 = (c == '/') ? 0 : (c == '*') ? 1 : 2;
		
		Branch b = this.successorFunc[state.ordinal()][idx2];
		
		if(b.getNewState() != this.NO_BRANCH)
			this.state = StateType.values()[b.getNewState()];
		else return false;
		
		if(b.save())
			tokenString += c;
		
		if(this.state == StateType.ERROR){
			
			this.currentToken = TokenType.ERROR;
			this.tokenString += ", Compiler bug";
			return false;
		}
		
		if(this.acceptState[this.state.ordinal()]){
			
			if(this.state == StateType.DONE){
				
				this.currentToken = TokenType.OVER;
				this.tokenString = "/";
				this.backtrack = true;
			}
		}
		
		return true;
	}
	
	/*
	 * Reinicia al automata.
	 */
	@Override
	public void restart(){
		
		this.state = StateType.START;
		this.tokenString = "";
		this.currentToken=null;
	}
	
	/*
	 * Indica si el estado actual es el inicial.
	 * 
	 * @return	<code>true</code> si es el estado inicial;
	 * 			<code>false</code> en caso contrario
	 */
	@Override
	public boolean inStartState(){
		
		return this.state == StateType.START;
	}
	
	/*
	 * Indica si el estado actual es de aceptacion.
	 * 
	 * @return	<code>true</code> si es estado de aceptacion;
	 * 			<code>false</code> en caso contrario
	 */
	@Override
	public boolean isDone(){
		
		return this.acceptState[this.state.ordinal()];
	}

}
